﻿using System;
using System.Collections.Generic;
using System.Collections;

namespace AiFrame.InterfaceLib.Data.Patterns
{
    /// <summary>
    /// Represents a list which fires events when changes occur.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class ObservableCollection<T>: IObservable, IEnumerable<T>, ISortableList<T>
    {
        /// <summary>
        /// The internal list.
        /// </summary>
        private List<T> collection;

        /// <summary>
        /// The observers.
        /// </summary>
        private List<IObserver> observerCollection;
        
        /// <summary>
        /// The update count.
        /// Every BeginUpdate increments this value.
        /// </summary>
        private int updateCount = 0;

        /// <summary>
        /// The constructor.
        /// </summary>
        public ObservableCollection()
        {
            observerCollection = new List<IObserver>();
            collection = new List<T>();
        }

        /// <summary>
        /// The item accessor.
        /// </summary>
        /// <param name="index">The index.</param>
        /// <returns>The item.</returns>
        public T this[int index]
        {
            get { return collection[index]; }
        }

        /// <summary>
        /// Adds a new item.
        /// </summary>
        /// <param name="item">The item</param>
        public void Add(T item)
        {
            collection.Add(item);
            NotifyObservers(item);
        }

        /// <summary>
        /// Returns the index of a specific item.
        /// </summary>
        /// <param name="item">The item.</param>
        /// <returns>The index of the given item.</returns>
        public int IndexOf(T item)
        {
            return collection.IndexOf(item);
        }

        /// <summary>
        /// Returns the number of the items.
        /// </summary>
        public int Count
        {
            get
            {
                return collection.Count;
            }
        }

        /// <summary>
        /// Sorts the list.
        /// </summary>
        public void Sort()
        {
            collection.Sort();
            NotifyObservers(null);
        }

        /// <summary>
        /// Removes an item.
        /// </summary>
        /// <param name="item">The item.</param>
        /// <returns>True, if the item has been removed.</returns>
        public bool Remove(T item)
        {
            bool result = collection.Remove(item);
            NotifyObservers(null);

            return result;
        }

        /// <summary>
        /// Removes an item at the given index.
        /// </summary>
        /// <param name="index">The index.</param>
        public void RemoveAt(int index)
        {
            collection.RemoveAt(index);
        }

        /// <summary>
        /// Fills the list with new elements after the old
        /// elements has been deleted.
        /// </summary>
        /// <param name="list"></param>
        public void Fill(ObservableCollection<T> list)
        {
            collection.Clear();
            foreach (T item in list)
            {
                collection.Add(item);
            }
            NotifyObservers(null);
            
        }
        
        #region IObservable Members

        public void NotifyObservers(Object obj)
        {
            if (updateCount == 0)
            {
                foreach (IObserver obs in observerCollection)
                {
                    obs.NotifyChanges(this, obj);
                }
            }
        }

        public void RegisterObserver(IObserver obs)
        {
            if (observerCollection.IndexOf(obs) == -1)
                observerCollection.Add(obs);
        }

        public void UnregisterObserver(IObserver obs)
        {
            observerCollection.Remove(obs);
        }

        /// <summary>
        /// Begins an update action.
        /// </summary>
        public void BeginUpdate()
        {
            updateCount += 1;
        }

        /// <summary>
        /// Ends an update action.
        /// </summary>
        public void EndUpdate()
        {
            updateCount -= 1;
            NotifyObservers(null);
        }

        #endregion



        #region IEnumerable Members
        
        IEnumerator<T> IEnumerable<T>.GetEnumerator()
        {
            for (int i = 0; i < collection.Count; i++)
                yield return collection[i];
        }

        public IEnumerator GetEnumerator()
        {
            for (int i = 0; i < collection.Count; i++)
                yield return collection[i];
        }

        #endregion

        #region Implementation of ISortableList<T>

        public void Sort(Comparison<T> comparison)
        {
            collection.Sort(comparison);
            NotifyObservers(null);
        }

        #endregion
    }
}